package com.github.xiaolyuh;

import com.github.xiaolyuh.configcenter.ConfigCenter;
import com.github.xiaolyuh.enums.LocalCacheInstanceEnum;
import com.github.xiaolyuh.utils.FastJSONHelper;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheStats;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.NumberFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 本地缓存配置管理
 *
 * @author xuwanglong
 */
public class LocalCacheConfigManager {

    private static final Logger logger = LoggerFactory.getLogger(LocalCacheConfigManager.class);

    public static LocalCacheConfigManager getInstance() {
        return CouponLocalCacheManagerHolder.LOCAL_CACHE_MANAGER;
    }

    /**
     * 从“技术”上来说，“使用枚举创建单例”是最严谨的实现方式，但从“语义”上来说显得很别扭。
     */
    private static final class CouponLocalCacheManagerHolder {

        private static final LocalCacheConfigManager LOCAL_CACHE_MANAGER = new LocalCacheConfigManager();

        private CouponLocalCacheManagerHolder() {
            // only for lazy initialize
        }
    }

    static {
        //普通coupon缓存(包含运营创建和用户领取的券详情)
        NORMAL_COUPON_LOCAL_CACHE = CacheBuilder.newBuilder()
                //缓存最大容量
                .maximumSize(ConfigCenter.getLocalCacheSizeForNormal())
                //读取失效时间
                .expireAfterAccess(ConfigCenter.getLocalCacheExpireTime(), TimeUnit.SECONDS)
                //写缓存并发数
                .concurrencyLevel(Math.max(12, Runtime.getRuntime().availableProcessors()))
                //缓存统计
                .recordStats()
                .build();

        //系统coupon缓存(包含运营创建的券详情、商品、店铺)
        SYSTEM_COUPON_LOCAL_CACHE = CacheBuilder.newBuilder()
                //缓存最大容量
                .maximumSize(ConfigCenter.getLocalCacheSizeForSystem())
                //读取失效时间
                .expireAfterAccess(ConfigCenter.getLocalCacheExpireTime(), TimeUnit.SECONDS)
                //写缓存并发数
                .concurrencyLevel(Math.max(12, Runtime.getRuntime().availableProcessors()))
                //缓存统计
                .recordStats()
                .build();

        ACTIVITY_LOCAL_CACHE = CacheBuilder.newBuilder()
                .maximumSize(ConfigCenter.getLocalCacheSizeForActivity())
                .expireAfterAccess(ConfigCenter.getLocalCacheExpireTime(), TimeUnit.SECONDS)
                .concurrencyLevel(Math.min(12, Runtime.getRuntime().availableProcessors()))
                .recordStats()
                .build();

        ACTIVITY_INDEX_LOCAL_CACHE = CacheBuilder.newBuilder()
                .maximumSize(ConfigCenter.getLocalCacheSizeForActivityIndex())
                .expireAfterAccess(ConfigCenter.getLocalCacheExpireTime(), TimeUnit.SECONDS)
                .concurrencyLevel(Math.min(12, Runtime.getRuntime().availableProcessors()))
                .recordStats()
                .build();
    }

    private static Cache<String, Optional<Object>> NORMAL_COUPON_LOCAL_CACHE;

    private static Cache<String, Optional<Object>> SYSTEM_COUPON_LOCAL_CACHE;

    /**
     * 活动+活动优先级
     */
    private static Cache<String, Optional<Object>> ACTIVITY_LOCAL_CACHE;

    /**
     * 活动索引
     */
    private static Cache<String, Optional<Object>> ACTIVITY_INDEX_LOCAL_CACHE;

    public Cache<String, Optional<Object>> getNormalCouponLocalCache() {
        return NORMAL_COUPON_LOCAL_CACHE;
    }

    public Cache<String, Optional<Object>> getSystemCouponLocalCache() {
        return SYSTEM_COUPON_LOCAL_CACHE;
    }

    public Cache<String, Optional<Object>> getActivityLocalCache() {
        return ACTIVITY_LOCAL_CACHE;
    }

    public Cache<String, Optional<Object>> getActivityIndexLocalCache() {
        return ACTIVITY_INDEX_LOCAL_CACHE;
    }

    private List<Cache<String, Optional<Object>>> caches() {
        return Lists.newArrayList(NORMAL_COUPON_LOCAL_CACHE, SYSTEM_COUPON_LOCAL_CACHE,
                ACTIVITY_LOCAL_CACHE, ACTIVITY_INDEX_LOCAL_CACHE);
    }

    public static Cache<String, Optional<Object>> getLocalCache(LocalCacheInstanceEnum instanceEnum){
        if(LocalCacheInstanceEnum.ACTIVITY_BASE_AND_PRIORITY.equals(instanceEnum)){
            return getInstance().getActivityLocalCache();
        }
        if(LocalCacheInstanceEnum.ACTIVITY_INDEX.equals(instanceEnum)){
            return getInstance().getActivityIndexLocalCache();
        }
        if(LocalCacheInstanceEnum.NORMAL_COUPON.equals(instanceEnum)){
            return getInstance().getNormalCouponLocalCache();
        }
        if(LocalCacheInstanceEnum.SYSTEM_COUPON.equals(instanceEnum)){
            return getInstance().getSystemCouponLocalCache();
        }
        return null;
    }

    /**
     * 查询本地缓存
     *
     * @param key
     * @return
     */
    public Object get(LocalCacheInstanceEnum instanceEnum,String key) {
        Cache<String, Optional<Object>> exactlyCache=getLocalCache(instanceEnum);
        List<Cache<String, Optional<Object>>> caches = Objects.isNull(exactlyCache)?caches(): Arrays.asList(exactlyCache);
        Object value = null;
        for (Cache<String, Optional<Object>> cache : caches) {
            Optional<Object> optional = cache.getIfPresent(key);
            if (optional != null && optional.isPresent()) {
                value = optional.get();
                break;
            }
        }
        return value;
    }

    /**
     * 失效本地缓存
     * @param instanceEnum
     * @param key
     * @return
     */
    public boolean invalidate(LocalCacheInstanceEnum instanceEnum,String key){
        Cache<String, Optional<Object>> exactlyCache=getLocalCache(instanceEnum);
        if(Objects.isNull(exactlyCache)){
            return false;
        }
        exactlyCache.invalidate(key);
        return true;
    }

    /**
     * 失效本地缓存
     * @param instanceEnum
     * @param keys
     * @return
     */
    public boolean invalidateAll(LocalCacheInstanceEnum instanceEnum,List<String> keys){
        Cache<String, Optional<Object>> exactlyCache=getLocalCache(instanceEnum);
        if(Objects.isNull(exactlyCache)){
            return false;
        }
        exactlyCache.invalidateAll(keys);
        return true;
    }

    /**
     * 缓存数据统计
     */
    public List<Map<String, Object>> stats() {
        if (!ConfigCenter.allowLocalCache()) {
            return Collections.emptyList();
        }
        Integer index = 1;
        List<Map<String, Object>> statsList = Lists.newArrayListWithExpectedSize(caches().size());
        List<Cache<String, Optional<Object>>> cacheList = caches();
        if (logger.isDebugEnabled()) {
            cacheList.stream().forEach(c -> logger.debug("缓存实例大小为：{}", c.size() ));
        }
        for (Cache<String, Optional<Object>> cache : cacheList) {
            statsList.add(stats(index, cache));
            index++;
        }
        return statsList;
    }

    private Map<String, Object> stats(Integer index, Cache cache) {
        Map<String, Object> map = new LinkedHashMap<>();
        CacheStats stats = cache.stats();
        //保留两位小数
        NumberFormat percent = NumberFormat.getPercentInstance();
        percent.setMaximumFractionDigits(2);
        //缓存数量
        map.put("cache_size", cache.size());
        map.put("request_count", stats.requestCount());
        //命中率
        map.put("hit_count", stats.hitCount());
        map.put("hit_rate", percent.format(stats.hitRate()));
        //丢失率
        map.put("miss_count", stats.missCount());
        map.put("miss_rate", percent.format(stats.missRate()));
        //淘汰数量(LRU)
        map.put("eviction_count", stats.evictionCount());
        logger.info("CacheStatsSummary[{}]:{}", index, FastJSONHelper.serialize(map));
        return map;
    }

}
